package com.lzy.authorizationServer.filter;

import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.service.CaptchaService;
import com.lzy.captcha.model.MyCaptchaVO;
import com.lzy.conf.I18nServiceImpl;
import com.lzy.conf.JSONResult;
import com.lzy.conf.RsaKeyProperties;
import com.lzy.exception.CaptchaExceotion;
import com.lzy.system.organization.entity.SysLoginLog;
import com.lzy.system.organization.entity.security.entity.SecurityUser;
import com.lzy.system.organization.service.ISysLoginLogService;
import com.lzy.utils.IPUtl;
import com.lzy.utils.JwtUtils;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.util.StreamUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Robod
 * @date 2020/8/10 7:54
 * 认证过滤器
 */
@Slf4j
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

    I18nServiceImpl i18nService = SpringUtil.getBean(I18nServiceImpl.class);
    ISysLoginLogService iSysLoginLogService = SpringUtil.getBean(ISysLoginLogService.class);

    private AuthenticationManager authenticationManager;
    private RsaKeyProperties rsaKeyProperties;
    private RedisTemplate redisTemplate;
    @Value("${jwt.expire}")
    private long expire;

    public JwtLoginFilter(AuthenticationManager authenticationManager, RsaKeyProperties rsaKeyProperties, long expire) {
        this.authenticationManager = authenticationManager;
        this.rsaKeyProperties = rsaKeyProperties;
        this.expire = expire;
        redisTemplate = (RedisTemplate) SpringUtil.getBean("redisTemplate");
    }

    /**
     * 这个方法是用来去尝试验证用户的，父类中是从POST请求的form表单中获取，但是这里不是，所以需要重写
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        MyCaptchaVO user = null;
        try {
            CaptchaService captchaService = SpringUtil.getBean(CaptchaService.class);

            ServletInputStream inputStream = request.getInputStream();
            String body = StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"));
            user = JSONObject.parseObject(body, MyCaptchaVO.class);
            ResponseModel captcha_ret = captchaService.verification(user);
            if (captcha_ret.isSuccess()) {
                return authenticationManager.authenticate(
                        new UsernamePasswordAuthenticationToken(
                                user.getUsername(),
                                user.getPassword())
                );
            } else {
                throw new CaptchaExceotion("验证失败请重试");
            }

        } catch (Exception e) {
            try {
                String error_msg = i18nService.getMessage("login.error.userIdOrPsdError");
                if (e instanceof CaptchaExceotion) {
                    error_msg = e.getMessage();
                }
                response.setContentType("application/json;charset=utf-8");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                PrintWriter out = response.getWriter();
                out.write(JSON.toJSONString(new JSONResult(HttpServletResponse.SC_UNAUTHORIZED, error_msg, null)));
                out.flush();
                out.close();
                log.error("[" + user.getUsername() + "]:" + error_msg);
            } catch (Exception e1) {
                throw new RuntimeException(e1);
            }
            throw new RuntimeException(e);
        }
    }

    /**
     * 成功之后执行的方法，父类中是放入session，不符合我们的要求，所以重写
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        SecurityUser securityUser = (SecurityUser) ((UsernamePasswordAuthenticationToken) authResult).getPrincipal();
        //24*60
        long time = expire;
        String token_uuid = IdUtil.simpleUUID();
        securityUser.setToken_uuid(token_uuid);
        String token = JwtUtils.generateTokenExpireInMinutes(securityUser, rsaKeyProperties.getPrivateKey(), (int) time);


        response.addHeader("Authorization", "lzy " + token);
        response.addHeader("Authorization_uuid", "lzy " + token_uuid);
        try {
            //登录成功时，返回json格式进行提示
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter out = response.getWriter();
            Map<String, Object> map = new HashMap<String, Object>(4);
            out.write(JSON.toJSONString(new JSONResult(HttpServletResponse.SC_OK, i18nService.getMessage("login.success"), null)));
            out.flush();
            out.close();

            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent"));

            //插入登录日志
            iSysLoginLogService.add(new SysLoginLog() {{
                setToken(token);
                setUsername(securityUser.getName());
                setLoginTime(new Date());
                setIp(IPUtl.getIpAddr(request));
                setUserid(authResult.getName());
                setDevice(userAgent.getOperatingSystem().toString());
                setBrowser(userAgent.getBrowser().toString());
                setUser_agent(request.getHeader("user-agent"));
                setToken_uuid(token_uuid);
            }});
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
}
